﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Input;
using System.Linq;

namespace MathTools {
    /// <summary>
    /// 数学表达式
    /// </summary>
    public class Sign {
        public static implicit operator Sign(double v) {
            var x = new Sign();
            x._default = v;
            x.Op = OpType.add;
            return x;
        }
        public static implicit operator Sign(int v) {
            var x = new Sign();
            x._default = v;
            x.Op = OpType.add;
            return x;
        }
        public static implicit operator Sign(float v) {
            var x = new Sign();
            x._default = v;
            x.Op = OpType.add;
            return x;
        }
        public static implicit operator Sign(short v) {
            var x = new Sign();
            x._default = v;
            x.Op = OpType.add;
            return x;
        }
        public static implicit operator Sign(string s) {
            Sign r = 0;
            var s2 = s.Replace(" ", string.Empty);
            var ss = s2.Split(new[] { '+', '-', '*', '/', '^', '&', '(', ')', ' ' }, StringSplitOptions.RemoveEmptyEntries);
            var len = ss.Length;
            var rs = new List<Sign>();
            var loc = ss[0].Length;
            for (int i = 1; i < len; i++) {
                var si = new Sign();
                var v = 0.0d;
                var b = double.TryParse(ss[i], out v);
                if (b) {
                    si._default = v;
                } else {
                    si.Name = ss[i];
                }
                var index = s2.IndexOf(ss[i], loc);
                loc = index + ss[i].Length;
                while (s2[--index] == '(') {
                    si.sequence++;
                }
                switch (s2[index]) {
                    case '+': si.Op = OpType.add; break;
                    case '-': si.Op = OpType.subtract; break;
                    case '*': si.Op = OpType.multiply; break;
                    case '/': si.Op = OpType.divide; break;
                    case '^': si.Op = OpType.pow; break;
                    case '&': si.Op = OpType.log; break;
                    default:
                        break;
                }
                if (index > 0) {
                    while (s2[--index] == ')') {
                        si.sequence--;
                    }
                }
                rs.Add(si);
            }
            var s0 = new Sign { Op = OpType.add };
            var v2 = 0.0d;
            var b2 = double.TryParse(ss[0], out v2);
            if (b2) {
                s0._default = v2;
            } else {
                s0.Name = ss[0];
            }
            rs.Insert(0, s0);
            var count = rs.Count;
            for (int i = count - 1; i >= 1; i--) {
                rs[i - 1].Add(rs[i]);
            }
            if (rs.Count > 0)
                r = rs[0];
            var rrs = r.Signs.ToArray();
            var clen = rrs.Length - 1;
            for (int i = 0; i < r.Count; i++) {
                var item = rrs[i];
                if (item.sequence < 0) {
                    item = item >> -item.sequence;
                } else if (item.sequence > 0) {
                    var mark = item.sequence;
                    item.sequence = 0;
                    item = item << mark;
                }
            }
            r.Expression = s2;
            return r;
        }

        public static Sign operator &(Sign v1, double v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.log;
            v1.Add(s);
            return v1;
        }
        public static Sign operator ^(Sign v1, double v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.pow;
            v1.Add(s);
            return v1;
        }
        public static Sign operator +(Sign v1, double v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.add;
            v1.Add(s);
            return v1;
        }
        public static Sign operator -(Sign v1, double v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.subtract;
            v1.Add(s);
            return v1;
        }
        public static Sign operator /(Sign v1, double v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.divide;
            v1.Add(s);
            return v1;
        }
        public static Sign operator *(Sign v1, double v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.multiply;
            v1.Add(s);
            return v1;
        }

        public static Sign operator &(Sign v1, int v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.log;
            v1.Add(s);
            return v1;
        }
        public static Sign operator ^(Sign v1, int v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.pow;
            v1.Add(s);
            return v1;
        }
        public static Sign operator +(Sign v1, int v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.add;
            v1.Add(s); ;
            return v1;
        }
        public static Sign operator -(Sign v1, int v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.subtract;
            v1.Add(s);
            return v1;
        }
        public static Sign operator *(Sign v1, int v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.multiply;
            v1.Add(s);
            return v1;
        }
        public static Sign operator /(Sign v1, int v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.divide;
            v1.Add(s);
            return v1;
        }

        public static Sign operator &(Sign v1, string v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.log;
            s.Name = v2;
            v1.Add(s);
            return v1;
        }
        public static Sign operator ^(Sign v1, string v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.pow;
            s.Name = v2;
            v1.Add(s);
            return v1;
        }
        public static Sign operator +(Sign v1, string v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.add;
            s.Name = v2;
            v1.Add(s);
            return v1;
        }
        public static Sign operator -(Sign v1, string v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.subtract;
            s.Name = v2;
            v1.Add(s);
            return v1;
        }
        public static Sign operator /(Sign v1, string v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.divide;
            s.Name = v2;
            v1.Add(s);
            return v1;
        }
        public static Sign operator *(Sign v1, string v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.multiply;
            s.Name = v2;
            v1.Add(s);
            return v1;
        }

        public static Sign operator &(Sign v1, float v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.log;
            v1.Add(s);
            return v1;
        }
        public static Sign operator ^(Sign v1, float v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.pow;
            v1.Add(s);
            return v1;
        }
        public static Sign operator +(Sign v1, float v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.add;
            v1.Add(s);
            return v1;
        }
        public static Sign operator -(Sign v1, float v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.subtract;
            v1.Add(s);
            return v1;
        }
        public static Sign operator /(Sign v1, float v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.divide;
            v1.Add(s);
            return v1;
        }
        public static Sign operator *(Sign v1, float v2) {
            Sign s = (Sign)v2;
            s.Op = OpType.multiply;
            v1.Add(s);
            return v1;
        }

        /*如果v1,v2的Count大于1的话，就要对v1,v2加外括号，如果具有外括号那么Add(Node) 方法要升级*/
        public static Sign operator &(Sign v1, Sign v2) {
            v2.Op = OpType.log;
            v1.Add(v2);
            return v1;
        }
        public static Sign operator ^(Sign v1, Sign v2) {
            v2.Op = OpType.pow;
            v1.Add(v2);
            return v1;
        }
        public static Sign operator +(Sign v1, Sign v2) {
            v2.Op = OpType.add;
            v1.Add(v2);
            return v1;
        }
        public static Sign operator -(Sign v1, Sign v2) {
            v2.Op = OpType.subtract;
            v1.Add(v2);
            return v1;
        }
        public static Sign operator /(Sign v1, Sign v2) {
            v1.Add(v2);
            v2.Op = OpType.divide;
            return v1;
        }
        public static Sign operator *(Sign v1, Sign v2) {
            v2.Op = OpType.multiply;
            v1.Add(v2);
            return v1;
        }

        public static Sign operator <<(Sign v, int i) {
            foreach (var item in v.Signs) {
                item.sequence += i;
            }
            return v;
        }
        public static Sign operator >>(Sign v, int i) {
            var mark = v.sequence;
            foreach (var item in v.Signs) {
                item.sequence -= i;
            }
            v.sequence = mark;
            return v;
        }
        public static implicit operator double(Sign sign) {
            Sign s = sign.ToExpression();
            do {
                var ss = s.SearchCurrentCalculatableNote();
                if (!s.M1(ss)) {
                    s.UpdateCalculateNodes(ss);
                    continue;
                }
                s.CalculateNode(s.SearchCurrentCalculatableNote(s.M3(ss)));
            } while (s.M2());
            return s._default;
        }

        public string GetExpression() {
            string v = null;
            var current = this;
            v = current.Name;
            while (current != null) {
                switch (current.Op) {
                    case OpType.add:
                        v += "+" + this.Name.ToString();
                        break;
                    case OpType.subtract:
                        v += "-" + this.Name.ToString();
                        break;
                    case OpType.multiply:
                        v += "*" + this.Name.ToString();
                        break;
                    case OpType.divide:
                        v += "/" + this.Name.ToString();
                        break;
                    case OpType.root:
                        break;
                    default:
                        break;
                }
                current = current.parent;
            }
            return v;
        }
        //
        Sign child, parent, top;
        dynamic _default;
        public int Count { get; set; }

        #region calculate
        bool isCalculated;
        int sequence;
        //int number;
        int GetOpLevel(OpType op) {
            var i = 0;
            switch (op) {
                case OpType.add: i = 1;
                    break;
                case OpType.subtract: i = 1;
                    break;
                case OpType.multiply: i = 2;
                    break;
                case OpType.divide: i = 2;
                    break;
                case OpType.log: i = 3;
                    break;
                case OpType.pow: i = 3;
                    break;
                case OpType.root: i = 0;
                    break;
                default:
                    break;
            }
            return i;
        }
        IEnumerable<Sign> SearchCurrentCalculatableNote() {
            var v = from item in Signs.ToArray()
                    where !item.isCalculated && item.parent != null
                    group item by item.sequence;
            var max = v.Max(t => t.Key);
            var r = default(IEnumerable<Sign>);
            foreach (var item in v) {
                if (item.Key == max) {
                    r = item; break;
                }
            }
            return r;
        }
        Sign SearchCurrentCalculatableNote(IEnumerable<Sign> ss) {
            var s = default(Sign);
            var v = from item in ss
                    group item by item.GetOpLevel(item.Op);
            var max = v.Max(t => t.Key);
            var vs = default(IEnumerable<Sign>);
            foreach (var item in v) {
                if (item.Key == max) {
                    vs = item; break;
                }
            }
            var minnum = vs.Min(t => t.Count);
            foreach (var item in vs) {
                if (item.Count == minnum) {
                    s = item; break;
                }
            }
            return s;
        }
        void UpdateCalculateNodes(IEnumerable<Sign> ss) {
            foreach (var item in ss) {
                item.sequence--;
            }
        }
        void CalculateNode(Sign v1, Sign v2) {
            switch (v1.Op) {
                case OpType.add:
                    v2._default += v1._default;
                    break;
                case OpType.subtract:
                    v2._default -= v1._default;
                    break;
                case OpType.multiply:
                    v2._default *= v1._default;
                    break;
                case OpType.divide:
                    v2._default /= v1._default;
                    break;
                case OpType.log:
                    v2._default = Math.Log(v2._default, v1._default);
                    break;
                case OpType.pow:
                    v2._default = Math.Pow(v2._default, v1._default);
                    break;
                case OpType.root:
                    break;
                default:
                    break;
            }
        }

        void CalculateNode(Sign s) {
            var v = default(Sign);
            v = s.parent;
            while (v.isCalculated) {
                v = v.parent;
            }
            if (s.sequence == v.sequence)
                CalculateNode(s, v);
            s.isCalculated = true;
        }
        bool M1(IEnumerable<Sign> ss) {
            var r = false;
            foreach (var item in ss) {
                if (item.parent.sequence >= item.sequence) {
                    r = true;
                    break;
                }
            }
            return r;
        }
        bool M2() {
            var r = true;
            var v = from item in Signs
                    where !item.isCalculated
                    select item;
            if (v.Count() == 1) {
                r = false;
            }
            return r;
        }
        void Reset() {
            foreach (var item in Signs) {
                item.isCalculated = false;
            }
        }
        IEnumerable<Sign> M3(IEnumerable<Sign> ss) {
            return from item in ss
                   where item.sequence <= item.parent.sequence
                   select item;
        }
        //        
        #endregion
        #region X
        public int Number { get; private set; }
        public double Max { get; private set; }
        public double Min { get; private set; }
        public Sign this[int index] {
            get { return Signs.ToArray()[Count - 1 - index]; }
        }
        public void Setting(double min, double max, int num, string name) {
            if (min >= max) {
                throw new Exception("setting min over max");
            }
            var x = from item in Signs where item.Name == name select item;
            foreach (var item in x) {
                item.Max = max; item.Min = min; item.Number = num;
            }
        }
        public Vector[] Parse(string name, int index = 0) {
            var vs = default(Vector[]);
            var xs = from item in Signs where item.Name == name select item;
            var s = xs.ElementAt(index);
            if (s != null && s.Number > 0) {
                var vl = new List<Vector>();
                double step = (s.Max - s.Min) / s.Number;
                int i = 0;
                var mark = s._default;
                for (var x = s.Min; x < s.Max; x += step) {
                    s._default = x;
                    double y = this;
                    vl.Add(new Vector(x, y));
                    i++;
                }
                vs = vl.ToArray();
                s._default = mark;
            }
            return vs;
        }
        #endregion
        #region node
        void Add(Sign v) {
            v.parent = this.child;
            this.top = v.top;
            this.Count = 1 + v.Count;
        }
        IEnumerable<Sign> Signs {
            get {
                var v = top;
                while (v != null && v != this) {
                    yield return v;
                    v = v.parent;
                }
                yield return this;
            }
        }
        void Remove(Sign v) {

        }
        void Destroy() {
        }
        #endregion
        OpType Op { get; set; }
        public string Name { get; set; }
        protected Sign() {
            //不能有显示的构造函数一切实例来源都是通过隐匿转换而来
            this.sequence = 0;
            this.Count = 1;
            this.isCalculated = false;
            top = child = this;//构造时确保child指向this
        }
        string Expression;
        string GetNodeString(Sign s, dynamic v) {
            var r = string.Empty;
            if (s.parent != null) {
                var i = s.sequence - s.parent.sequence;
                if (i > 0) {
                    for (int j = 0; j < i; j++) {
                        r += "(";
                    }
                    r = string.Format("{0}{1}{2}", s.Op.ToChar(), r, v);
                } else if (i < 0) {
                    for (int j = 0; j < -i; j++) {
                        r += ")";
                    }
                    r = string.Format("{0}{1}{2}", r, s.Op.ToChar(), v);
                } else {
                    r = string.Format("{0}{1}", s.Op.ToChar(), v);
                }
            } else {
                r = string.Format("{0}", v);
            }
            return r;
        }
        string GetNodeString3(Sign s, dynamic v) {
            var r = string.Empty;
            if (s.parent != null) {
                var i = s.sequence - s.parent.sequence;
                if (i > 0) {
                    for (int j = 0; j < i; j++) {
                        r += "(";
                    }
                    r = string.Format("{0}{1}{2}", s.Op.ToChar(), r, v);
                } else if (i < 0) {
                    for (int j = 0; j < -i; j++) {
                        r += ")";
                    }
                    r = string.Format("{0}{1}{2}", r, s.Op.ToChar(), v);
                } else {
                    r = string.Format("{0}{1}", s.Op.ToChar(), v);
                }
            } else {
                r = string.Format("{0}", v);
            }
            if (s.Count == 1) {
                var i = s.sequence;
                for (int j = 0; j < i; j++) {
                    r += ")";
                }
            }
            return r;
        }
        string GetNodeString2(Sign s, string[] names) {
            var r = string.Empty;
            if (names.Contains(s.Name)) {
                r = GetNodeString3(s, s.Name);
            } else {
                r = GetNodeString3(s, s._default);
            }
            return r;
        }
        public override string ToString() {
            var ss = Signs.ToArray();
            var count = ss.Length;
            var expr = string.Empty;
            for (int i = count - 1; i >= 0; i--) {
                expr += GetNodeString3(ss[i], ss[i].Name);
            }
            return expr;
        }
        public string ToString(params string[] names) {
            var ss = Signs.ToArray();
            var count = ss.Length;
            var expr = string.Empty;
            for (int i = count - 1; i >= 0; i--) {
                expr += GetNodeString2(ss[i], names);
            }
            return expr;
        }
        string ToExpression() {
            var ss = Signs.ToArray();
            var count = ss.Length;
            this.Expression = string.Empty;
            for (int i = count - 1; i >= 0; i--) {
                this.Expression += GetNodeString(ss[i], ss[i]._default);
            }
            return this.Expression;
        }
    }
    public enum OpType {
        /// <summary>
        /// +
        /// </summary>
        add,
        /// <summary>
        /// -
        /// </summary>
        subtract,
        /// <summary>
        /// *
        /// </summary>
        multiply,
        /// <summary>
        /// /
        /// </summary>
        divide,
        /// <summary>
        /// &
        /// </summary>
        log,
        /// <summary>
        /// ^
        /// </summary>
        pow,
        root,
    }
    static class Ex {
        public static string ToChar(this OpType op) {
            var r = string.Empty;
            switch (op) {
                case OpType.add: r = "+";
                    break;
                case OpType.subtract: r = "-";
                    break;
                case OpType.multiply: r = "*";
                    break;
                case OpType.divide: r = "/";
                    break;
                case OpType.log: r = "&";
                    break;
                case OpType.pow: r = "^";
                    break;
                default:
                    break;
            }
            return r;
        }
    }
}
